package com.hg.distributedlock.redis;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author: skh
 * @Date: 2019/10/14 10:07
 * @Description:
 */
@Service
@Slf4j
public class ActivityService {

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    //synchronized只能用于单机版
    public synchronized void getRedPacket(Integer userId) {
        log.info("开始抢红包");

        //先判断用户是否抢过红包
        EntityWrapper<ActivityDO> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id", userId);
        List<ActivityDO> activityDOS = activityMapper.selectList(wrapper);
        if (CollUtil.isNotEmpty(activityDOS)) {
            return;
        }

        //如果没抢过,则插入抢红包记录
        ActivityDO activityDO = new ActivityDO();
        activityDO.setUserId(userId);
        activityDO.setRedPacketId(RandomUtil.simpleUUID());
        activityMapper.insert(activityDO);
        log.info("插入红包记录成功");
    }


    /**
     * 使用redis
     * 缺点:需要设置定时器监控锁是否过期
     *
     * @param userId
     */
    public void getRedPacketV2(Integer userId) {
        String redPacketId = RandomUtil.simpleUUID();
        //加锁并设置过期时间,保证该原子性
        String key = "red:packet:userId:";
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key + userId, redPacketId, 60, TimeUnit.SECONDS);
        if (!result) {
            log.info("加锁失败");
            return;
        }
        try {
            log.info("开始抢红包");

            //先判断用户是否抢过红包
            EntityWrapper<ActivityDO> wrapper = new EntityWrapper<>();
            wrapper.eq("user_id", userId);
            List<ActivityDO> activityDOS = activityMapper.selectList(wrapper);
            if (CollUtil.isNotEmpty(activityDOS)) {
                log.info("用户已抢红包");
                return;
            }

            //如果没抢过,则插入抢红包记录
            ActivityDO activityDO = new ActivityDO();
            activityDO.setUserId(userId);
            activityDO.setRedPacketId(redPacketId);
            activityMapper.insert(activityDO);
            log.info("插入红包记录成功");
        } finally {
            //保证自己只能解自己的锁.
            if (redPacketId.equals(stringRedisTemplate.opsForValue().get(key + userId))) {
                stringRedisTemplate.delete(key + userId);
            }
        }
    }

    /**
     * 使用redisson
     * 缺点:主从模式下,会有锁失效的问题
     *
     * @param userId
     */
    public void getRedPacketV3(Integer userId) {
        String redPacketId = RandomUtil.simpleUUID();
        //加锁并设置过期时间,保证该原子性
        String key = "red:packet:userId:";
        RLock lock = redissonClient.getLock(key + userId);
        boolean result = false;
        try {
            //Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key + userId, redPacketId, 60, TimeUnit.SECONDS);
            if (!lock.tryLock(0, 60, TimeUnit.SECONDS)) {
                log.info("加锁失败");
                return;
            }
            try {
                log.info("开始抢红包");

                //先判断用户是否抢过红包
                EntityWrapper<ActivityDO> wrapper = new EntityWrapper<>();
                wrapper.eq("user_id", userId);
                List<ActivityDO> activityDOS = activityMapper.selectList(wrapper);
                if (CollUtil.isNotEmpty(activityDOS)) {
                    log.info("用户已抢红包");
                    return;
                }

                //如果没抢过,则插入抢红包记录
                ActivityDO activityDO = new ActivityDO();
                activityDO.setUserId(userId);
                activityDO.setRedPacketId(redPacketId);
                activityMapper.insert(activityDO);
                log.info("插入红包记录成功");
            } finally {
                //保证自己只能解自己的锁.
                if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                    lock.unlock();
                }
                //if (redPacketId.equals(stringRedisTemplate.opsForValue().get(key + userId))) {
                //    stringRedisTemplate.delete(key + userId);
                //}

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
    }

    public void emptyTable() {
        activityMapper.delete(null);
        log.info("清除记录成功");
    }
}
